// Shader.h
//
#ifndef SHADER_ONCE
#define SHADER_ONCE

#include "OpenGL.h"

#include <string>


namespace OpenGL {
/*
class Shader
{
protected:
	GLuint shaderID;
	GLenum shaderType;

	std::string code;

	bool compiled;

public:
	// ------------------------------------------------------------------------------------------------
	Shader( const std::string &s = std::string(""), GLenum type = GL_NONE )
	: compiled(false)
	{
		set(s,type);
	}
	
	// ------------------------------------------------------------------------------------------------
	virtual ~Shader();

	// ------------------------------------------------------------------------------------------------
	void set( const std::string &s, GLenum type )
	{
		code       = s; 
		shaderType = type; 
		
		shaderID = glCreateShader(shaderType);
		const char *s = code.c_str();
		glShaderSource(shaderID, 1, &s, 0);
		glCompileShader(shaderID);

		int status;
		glGetShaderiv( shaderID, GL_COMPILE_STATUS, &status );
		compiled = status == GL_TRUE;
	}
};
*/

class Shader
{
protected:
	GLuint vshd;
	GLuint gshd;
	GLuint fshd;
	GLuint prog;
	bool linked;

	void init()
	{
		vshd = 0;
		gshd = 0;
		fshd = 0;
		prog = 0;
		linked = false;
	}

public :
	static bool isSupported()
	{
		return glewIsSupported("GL_ARB_shader_objects");
	}


	static void bindFixedPipeline()	{glUseProgram(0);}

	Shader()	        {init();}
	virtual ~Shader()	{clear();}

	// ------------------------------------------------------------------------------------------------
	void set(const std::string & vertex_source)
	{
		clear();

		{
			vshd = glCreateShader(GL_VERTEX_SHADER);
			const char * s = vertex_source.c_str();
			glShaderSource(vshd, 1, &s, 0);
			glCompileShader(vshd);
		}
		
		{
			prog = glCreateProgram();
			glAttachShader(prog, vshd);
			glLinkProgram(prog);
		}

		GLint ls = 0;
		glGetProgramiv(prog, GL_LINK_STATUS, &ls);

		linked = (ls != GL_FALSE);
	}


	// ------------------------------------------------------------------------------------------------
	void set(const std::string & vertex_source, const std::string & fragment_source)
	{
		clear();

		{
			this->vshd = glCreateShader(GL_VERTEX_SHADER);
			const char * s = vertex_source.c_str();
			glShaderSource(this->vshd, 1, &s, 0);
			glCompileShader(this->vshd);
		}

		{
			this->fshd = glCreateShader(GL_FRAGMENT_SHADER);
			const char * s = fragment_source.c_str();
			glShaderSource(this->fshd, 1, &s, 0);
			glCompileShader(this->fshd);
		}

		{
			this->prog = glCreateProgram();
			glAttachShader(this->prog, this->vshd);
			glAttachShader(this->prog, this->fshd);
			glLinkProgram(this->prog);
		}

		GLint ls = 0;
		glGetProgramiv(this->prog, GL_LINK_STATUS, &ls);

		this->linked = (ls != GL_FALSE);
	}

	// ------------------------------------------------------------------------------------------------
	void setFromFile(const std::string &vtx_filename, const std::string &frg_filename)
	{
		clear();
		FILE *f;
		{
			// Load vertex shader
			f = fopen(vtx_filename.c_str(), "rb");
			if (!f) return;

			fseek(f, 0, SEEK_END);
			const int sz = (int)ftell(f);
			rewind(f);

			char * src = new char[sz + 1];
			fread(src, sizeof(char), sz, f);
			fclose(f);
			src[sz] = '\0';

			vshd = glCreateShader(GL_VERTEX_SHADER);
			const char *s = src;
			glShaderSource(vshd, 1, &s, 0);
			glCompileShader(vshd);

			delete [] src;
		}

		{
			// Load vertex shader
			f = fopen(frg_filename.c_str(), "rb");
			if (!f) return;

			fseek(f, 0, SEEK_END);
			const int sz = (int)ftell(f);
			rewind(f);

			char * src = new char[sz + 1];
			fread(src, sizeof(char), sz, f);
			fclose(f);
			src[sz] = '\0';

			fshd = glCreateShader(GL_FRAGMENT_SHADER);
			const char *s = src;
			glShaderSource(fshd, 1, &s, 0);
			glCompileShader(fshd);

			delete [] src;
		}

		{
			prog = glCreateProgram();
			glAttachShader(prog, vshd);
			glAttachShader(prog, fshd);
			glLinkProgram(prog);
		}

		GLint ls = 0;
		glGetProgramiv(prog, GL_LINK_STATUS, &ls);

		linked = (ls != GL_FALSE);
	}

	// ------------------------------------------------------------------------------------------------
	void set(const std::string & vertex_source, const std::string & geometry_source, const std::string & fragment_source, GLenum geometry_in_type, GLenum geometry_out_type, GLint max_out_vertices)
	{
		this->clear();

		{
			this->vshd = glCreateShader(GL_VERTEX_SHADER);
			const char * s = vertex_source.c_str();
			glShaderSource(this->vshd, 1, &s, 0);
			glCompileShader(this->vshd);
		}

		{
			this->fshd = glCreateShader(GL_FRAGMENT_SHADER);
			const char * s = fragment_source.c_str();
			glShaderSource(this->fshd, 1, &s, 0);
			glCompileShader(this->fshd);
		}

		{
			this->gshd = glCreateShader(GL_GEOMETRY_SHADER_EXT);
			const char * s = geometry_source.c_str();
			glShaderSource(this->gshd, 1, &s, 0);
			glCompileShader(this->gshd);
		}

		{
			this->prog = glCreateProgram();

			glAttachShader(this->prog, this->vshd);
			glAttachShader(this->prog, this->fshd);
			glAttachShader(this->prog, this->gshd);

			glProgramParameteriEXT(this->prog, GL_GEOMETRY_INPUT_TYPE_EXT,   geometry_in_type);
			glProgramParameteriEXT(this->prog, GL_GEOMETRY_OUTPUT_TYPE_EXT,  geometry_out_type);
			glProgramParameteriEXT(this->prog, GL_GEOMETRY_VERTICES_OUT_EXT, max_out_vertices);

			glLinkProgram(this->prog);
		}

		GLint ls = 0;
		glGetProgramiv(this->prog, GL_LINK_STATUS, &ls);

		this->linked = (ls != GL_FALSE);

		return;
	}

	void clear()
	{
		if (this->prog == 0) return;

		glDeleteProgram(this->prog);
		glDeleteShader(this->vshd);
		glDeleteShader(this->fshd);

		if (this->gshd != 0)
		{
			glDeleteShader(this->gshd);
		}

		this->init();
	}

	bool isValid() const		{return ((this->prog != 0) && this->isLinked());}
	bool isLinked() const		{return this->linked;}

	const std::string getInfoLog() const
	{
		GLint len = 0;

		glGetProgramiv(this->prog, GL_INFO_LOG_LENGTH, &len);

		char * ch = new char[len + 1];

		glGetProgramInfoLog(this->prog, len, &len, ch);
		ch[len] = '\0';
		std::string log = ch;

		delete [] ch;

		return log;
	}

	void bind()	{glUseProgram(this->prog);}

	GLint uniformLocation(const char * name)                                    { return glGetUniformLocation(this->prog, name); }

	// Single valued (int)
	void uniform(const char * name, GLint x)                                    { uniform(uniformLocation(name), x); }
	void uniform(const char * name, GLint x, GLint y)                           { uniform(uniformLocation(name), x, y); }
	void uniform(const char * name, GLint x, GLint y, GLint z)                  { uniform(uniformLocation(name), x, y, z); }
	void uniform(const char * name, GLint x, GLint y, GLint z, GLint w)         { uniform(uniformLocation(name), x, y, z, w); }

	void uniform(GLint location, GLint x)                                       { glUniform1i(location, x); }
	void uniform(GLint location, GLint x, GLint y)                              { glUniform2i(location, x, y); }
	void uniform(GLint location, GLint x, GLint y, GLint z)                     { glUniform3i(location, x, y, z); }
	void uniform(GLint location, GLint x, GLint y, GLint z, GLint w)            { glUniform4i(location, x, y, z, w); }

	// Array values (int) 
	void uniform1v(const char * name, GLint size, const GLint * x)              { uniform1v(uniformLocation(name), size, x); }
	void uniform2v(const char * name, GLint size, const GLint * xy)             { uniform2v(uniformLocation(name), size, xy); }
	void uniform3v(const char * name, GLint size, const GLint * xyz)            { uniform3v(uniformLocation(name), size, xyz); }
	void uniform4v(const char * name, GLint size, const GLint * xyzw)			{ uniform4v(uniformLocation(name), size, xyzw); }

	void uniform1v(GLint location, GLint size, const GLint * x)					{ glUniform1iv(location, size, x); }
	void uniform2v(GLint location, GLint size, const GLint * xy)                { glUniform2iv(location, size, xy); }
	void uniform3v(GLint location, GLint size, const GLint * xyz)               { glUniform3iv(location, size, xyz); }
	void uniform4v(GLint location, GLint size, const GLint * xyzw)              { glUniform4iv(location, size, xyzw); }

	// Single valued (float)
	void uniform(const char * name, GLfloat x)                                  { uniform(uniformLocation(name), x); }
	void uniform(const char * name, GLfloat x, GLfloat y)                       { uniform(uniformLocation(name), x, y); }
	void uniform(const char * name, GLfloat x, GLfloat y, GLfloat z)            { uniform(uniformLocation(name), x, y, z); }
	void uniform(const char * name, GLfloat x, GLfloat y, GLfloat z, GLfloat w) { uniform(uniformLocation(name), x, y, z, w); }

	void uniform(GLint location, GLfloat x)                                     { glUniform1f(location, x); }
	void uniform(GLint location, GLfloat x, GLfloat y)                          { glUniform2f(location, x, y); }
	void uniform(GLint location, GLfloat x, GLfloat y, GLfloat z)               { glUniform3f(location, x, y, z); }
	void uniform(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w)    { glUniform4f(location, x, y, z, w); }

	// Array values (float)
	void uniform1v(const char * name, GLint size, const GLfloat * x)            { uniform1v(uniformLocation(name), size, x); }
	void uniform2v(const char * name, GLint size, const GLfloat * xy)           { uniform2v(uniformLocation(name), size, xy); }
	void uniform3v(const char * name, GLint size, const GLfloat * xyz)          { uniform3v(uniformLocation(name), size, xyz); }
	void uniform4v(const char * name, GLint size, const GLfloat * xyzw)         { uniform4v(uniformLocation(name), size, xyzw); }

	void uniform1v(GLint location, GLint size, const GLfloat * x)               { glUniform1fv(location, size, x); }
	void uniform2v(GLint location, GLint size, const GLfloat * xy)              { glUniform2fv(location, size, xy); }
	void uniform3v(GLint location, GLint size, const GLfloat * xyz)             { glUniform3fv(location, size, xyz); }
	void uniform4v(GLint location, GLint size, const GLfloat * xyzw)            { glUniform4fv(location, size, xyzw); }
};

	//class surface
	//{
	//	public :

	//		typedef surface this_type;

	//		static void bind_write_screen(void)
	//		{
	//			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
	//		}

	//		virtual ~surface(void)
	//		{
	//			this->clear();
	//		}

	//		surface(void)
	//		{
	//			this->initialize_values();
	//		}

	//		surface(const this_type & s)
	//		{
	//			(void)s;
	//			this->initialize_values();
	//			this->copy_from(s);
	//		}

	//		void set(int width, int height, GLenum color_format, GLenum depth_format, bool generate_mipmap)
	//		{
	//			if (this->is_valid())
	//			{
	//				if ((this->width == width) && (this->height == height) && (this->color_format == color_format) && (this->depth_format == depth_format))
	//				{
	//					return;
	//				}
	//			}

	//			this->clear();

	//			if ((width <= 0) || (height <= 0) || (color_format == GL_NONE))
	//			{
	//				return;
	//			}

	//			glGenTextures(1, &(this->color_tex));
	//			glBindTexture(GL_TEXTURE_2D, this->color_tex);
	//			glTexImage2D(GL_TEXTURE_2D, 0, color_format, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
	//			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	//			glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

	//			if (generate_mipmap)
	//			{
	//				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	//				glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
	//				glGenerateMipmapEXT(GL_TEXTURE_2D);
	//			}
	//			else
	//			{
	//				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	//				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	//			}

	//			glBindTexture(GL_TEXTURE_2D, 0);

	//			if (depth_format != GL_NONE)
	//			{
	//				glGenTextures(1, &(this->depth_tex));
	//				glBindTexture(GL_TEXTURE_2D, this->depth_tex);
	//				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	//				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	//				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
	//				glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
	//				//glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE_ARB, GL_LUMINANCE);
	//				//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE_ARB);
	//				//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
	//				glTexImage2D(GL_TEXTURE_2D, 0, depth_format, width, height, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT, 0);
	//				glBindTexture(GL_TEXTURE_2D, 0);
	//			}

	//			glGenFramebuffersEXT(1, &(this->fbo));
	//			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, this->fbo);

	//			glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, this->color_tex, 0);

	//			if (depth_format != GL_NONE)
	//			{
	//				glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, this->depth_tex, 0);
	//			}

	//			const GLenum s = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
	//			const bool res = (s == GL_FRAMEBUFFER_COMPLETE_EXT);

	//			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

	//			if (!res)
	//			{
	//				glDeleteFramebuffersEXT(1, &(this->fbo));
	//				this->fbo = 0;

	//				glDeleteTextures(1, &(this->color_tex));
	//				this->color_tex = 0;

	//				glDeleteTextures(1, &(this->depth_tex));
	//				this->depth_tex = 0;

	//				return;
	//			}

	//			this->width  = width;
	//			this->height = height;

	//			this->color_format = color_format;
	//			this->depth_format = depth_format;
	//		}

	//		void clear(void)
	//		{
	//			if (!this->is_valid()) return;

	//			glDeleteFramebuffersEXT(1, &(this->fbo));

	//			glDeleteTextures(1, &(this->color_tex));

	//			if (this->depth_tex != 0)
	//			{
	//				glDeleteTextures(1, &(this->depth_tex));
	//			}

	//			this->initialize_values();
	//		}

	//		bool is_valid(void) const
	//		{
	//			return (this->fbo != 0);
	//		}

	//		int get_width(void) const
	//		{
	//			return this->width;
	//		}

	//		int get_height(void) const
	//		{
	//			return this->height;
	//		}

	//		GLenum get_color_format(void) const
	//		{
	//			return this->color_format;
	//		}

	//		GLenum get_depth_format(void) const
	//		{
	//			return this->depth_format;
	//		}

	//		void bind_color(void)
	//		{
	//			glBindTexture(GL_TEXTURE_2D, this->color_tex);
	//		}

	//		void bind_depth(void)
	//		{
	//			glBindTexture(GL_TEXTURE_2D, this->depth_tex);
	//		}

	//		void bind_write(void)
	//		{
	//			glViewport(0, 0, this->width, this->height);

	//			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, this->fbo);
	//		}

	//		void read_color(GLenum format, GLenum type, void * pixels)
	//		{
	//			glBindTexture(GL_TEXTURE_2D, this->color_tex);
	//			glGetTexImage(GL_TEXTURE_2D, 0, format, type, pixels);
	//			glBindTexture(GL_TEXTURE_2D, 0);
	//		}

	//		void read_depth(float * pixels)
	//		{
	//			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, this->fbo);
	//			glReadPixels(0, 0, this->width, this->height, GL_DEPTH_COMPONENT, GL_FLOAT, pixels);
	//			glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
	//		}

	//		this_type & operator = (const this_type & s)
	//		{
	//			this->copy_from(s);
	//			return (*this);
	//		}

	//		operator bool (void) const
	//		{
	//			return this->is_valid();
	//		}

	//	protected :

	//		int width;
	//		int height;
	//		GLuint fbo;
	//		GLenum color_format;
	//		GLuint color_tex;
	//		GLenum depth_format;
	//		GLuint depth_tex;

	//		void initialize_values(void)
	//		{
	//			this->width  = 0;
	//			this->height = 0;
	//			this->fbo    = 0;
	//			this->color_format = GL_NONE;
	//			this->color_tex    = 0;
	//			this->depth_format = GL_NONE;
	//			this->depth_tex    = 0;
	//		}

	//		void copy_from(const this_type & s)
	//		{
	//			(void)s;
	//			this->clear();
	//		}
	//};

	//class buffer
	//{
	//	public :

	//		typedef buffer this_type;

	//		static void bind_screen_draw_pixels(void)
	//		{
	//			glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0);
	//		}

	//		static void bind_screen_read_pixels(void)
	//		{
	//			glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, 0);
	//		}

	//		static void bind_client_array(void)
	//		{
	//			glBindBuffer(GL_ARRAY_BUFFER_ARB, 0);
	//		}

	//		static void bind_client_elements(void)
	//		{
	//			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, 0);
	//		}

	//		virtual ~buffer(void)
	//		{
	//			this->clear();
	//		}

	//		buffer(void)
	//		{
	//			this->initialize_values();
	//		}

	//		buffer(const this_type & b)
	//		{
	//			this->initialize_values();
	//			this->copy_from(b);
	//		}

	//		void set(int size, GLenum mode)
	//		{
	//			this->clear();

	//			glGenBuffers(1, &(this->buff));

	//			glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, this->buff);
	//			glBufferData(GL_PIXEL_PACK_BUFFER_ARB, size, 0, mode);

	//			glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0);

	//			this->size = size;
	//		}

	//		void clear(void)
	//		{
	//		}

	//		bool is_valid(void) const
	//		{
	//			return (this->buff != 0);
	//		}

	//		int get_size(void) const
	//		{
	//			return this->size;
	//		}

	//		void resize(int size, GLenum mode)
	//		{
	//			glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, this->buff);
	//			glBufferData(GL_PIXEL_PACK_BUFFER_ARB, size, 0, mode);

	//			glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, 0);

	//			this->size = size;
	//		}

	//		void bind_read_pixels_destination(void)
	//		{
	//			glBindBuffer(GL_PIXEL_PACK_BUFFER_ARB, this->buff);
	//		}

	//		void bind_draw_pixels_source(void)
	//		{
	//			glBindBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, this->buff);
	//		}

	//		void bind_array(void)
	//		{
	//			glBindBuffer(GL_ARRAY_BUFFER_ARB, this->buff);
	//		}

	//		void bind_elements(void)
	//		{
	//			glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, this->buff);
	//		}

	//	protected:

	//		GLuint buff;
	//		int size;

	//		void initialize_values(void)
	//		{
	//			this->buff = 0;
	//			this->size = 0;
	//		}

	//		void copy_from(const this_type & b)
	//		{
	//			(void)b;
	//			this->clear();
	//		}
	//};

	//class utils
	//{
	//	public :

	//		typedef utils this_type;

	//		static void save_ppm(const char * file_name, int w, int h, const unsigned char * pixels)
	//		{
	//			char buff[512];

	//			sprintf(buff, "P6\n%d %d\n255\n", w, h);

	//			FILE * f = fopen(file_name, "wb");
	//			fwrite(buff, sizeof(char), strlen(buff), f);
	//			fwrite(pixels, sizeof(unsigned char), w * h * 3, f);
	//			fclose(f);
	//		}

	//		class draw
	//		{
	//			public :

	//				typedef draw this_type;

	//				static void quad(void)
	//				{
	//					glBegin(GL_QUADS);
	//						glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 0.0f);
	//						glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 0.0f);
	//						glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f,  1.0f, 0.0f);
	//						glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f,  1.0f, 0.0f);
	//					glEnd();
	//				}

	//				static void triad(void)
	//				{
	//					glBegin(GL_LINES);
	//						glColor3f(1.0f, 0.0f, 0.0f);
	//						glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(1.0f, 0.0f, 0.0f);

	//						glColor3f(0.0f, 1.0f, 0.0f);
	//						glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(0.0f, 1.0f, 0.0f);

	//						glColor3f(0.0f, 0.0f, 1.0f);
	//						glVertex3f(0.0f, 0.0f, 0.0f); glVertex3f(0.0f, 0.0f, 1.0f);
	//					glEnd();
	//				}

	//				static void show_surface_color(surface & srf)
	//				{
	//					static bool do_init = true;
	//					static program copy_prog;

	//					if (do_init)
	//					{
	//						do_init = false;

	//						programs::create::copy(copy_prog);
	//					}

	//					copy_prog.bind();
	//					copy_prog.uniform("u_texture", GLint(0));

	//					srf.bind_color();

	//					draw::quad();
	//				}

	//				static void show_surface_depth(surface & srf)
	//				{
	//					static bool do_init = true;
	//					static program copy_prog;

	//					if (do_init)
	//					{
	//						do_init = false;

	//						programs::create::copy(copy_prog);
	//					}

	//					copy_prog.bind();
	//					copy_prog.uniform("u_texture", GLint(0));

	//					srf.bind_depth();

	//					draw::quad();
	//				}

	//				static void show_surface_color(surface & srf, int x, int y, int width, int height)
	//				{
	//					glPushAttrib(GL_VIEWPORT_BIT);

	//					glViewport((GLint)x, (GLint)y, (GLsizei)width, (GLsizei)height);

	//					this_type::show_surface_color(srf);

	//					glPopAttrib();
	//				}

	//				static void show_surface_depth(surface & srf, int x, int y, int width, int height)
	//				{
	//					glPushAttrib(GL_VIEWPORT_BIT);

	//					glViewport((GLint)x, (GLint)y, (GLsizei)width, (GLsizei)height);

	//					this_type::show_surface_depth(srf);

	//					glPopAttrib();
	//				}

	//				static void show_surface_color(surface & srf, int x, int y)
	//				{
	//					this_type::show_surface_color(srf, x, y, srf.get_width(), srf.get_height());
	//				}

	//				static void show_surface_depth(surface & srf, int x, int y)
	//				{
	//					this_type::show_surface_depth(srf, x, y, srf.get_width(), srf.get_height());
	//				}

	//				static void save_surface_color(const char * file_name, surface & srf)
	//				{
	//					unsigned char * pixels = new unsigned char [srf.get_width() * srf.get_height() * 3];
	//					srf.read_color(GL_RGB, GL_UNSIGNED_BYTE, pixels);
	//					save_ppm(file_name, srf.get_width(), srf.get_height(), pixels);
	//					delete [] pixels;
	//				}

	//				static void save_surface_depth(const char * file_name, surface & srf)
	//				{
	//					const size_t sz = srf.get_width() * srf.get_height();
	//					float * fpixels = new float [sz];
	//					srf.read_depth(fpixels);

	//					unsigned char * pixels = new unsigned char [sz * 3];
	//					size_t k = 0;
	//					for (size_t i=0; i<sz*3; i+=3)
	//					{
	//						const unsigned char v = (unsigned char)(fpixels[k] * 255.0f);
	//						pixels[i+0] = v;
	//						pixels[i+1] = v;
	//						pixels[i+2] = v;
	//						++k;
	//					}
	//					delete [] fpixels;

	//					save_ppm(file_name, srf.get_width(), srf.get_height(), pixels);

	//					delete [] pixels;
	//				}
	//		};

	//		class programs
	//		{
	//			public :

	//				typedef programs this_type;

	//				class create
	//				{
	//					public :

	//						typedef create this_type;

	//						static void copy(program & p)
	//						{
	//							const std::string vsrc = GL_WRAP_STRINGFY
	//							(
	//								varying vec2 v_texcoord;

	//								void main(void)
	//								{
	//									v_texcoord  = gl_MultiTexCoord0.xy;
	//									gl_Position = gl_Vertex;
	//								}
	//							);

	//							const std::string fsrc = GL_WRAP_STRINGFY
	//							(
	//								uniform sampler2D u_texture;

	//								varying vec2 v_texcoord;

	//								void main(void)
	//								{
	//									gl_FragColor = texture2D(u_texture, v_texcoord);
	//								}
	//							);

	//							p.set(vsrc, fsrc);
	//						}

	//						static void bias(program & p)
	//						{
	//							const std::string vsrc = GL_WRAP_STRINGFY
	//							(
	//								varying vec2 v_texcoord;

	//								void main(void)
	//								{
	//									v_texcoord  = gl_MultiTexCoord0.xy;
	//									gl_Position = gl_Vertex;
	//								}
	//							);

	//							const std::string fsrc = GL_WRAP_STRINGFY
	//							(
	//								uniform sampler2D u_texture;
	//								uniform float     u_bias;

	//								varying vec2 v_texcoord;

	//								void main(void)
	//								{
	//									gl_FragColor = vec4(u_bias) * texture2D(u_texture, v_texcoord);
	//								}
	//							);

	//							p.set(vsrc, fsrc);
	//						}

	//						static void scale(program & p)
	//						{
	//							const std::string vsrc = GL_WRAP_STRINGFY
	//							(
	//								varying vec2 v_texcoord;

	//								void main(void)
	//								{
	//									v_texcoord  = gl_MultiTexCoord0.xy;
	//									gl_Position = gl_Vertex;
	//								}
	//							);

	//							const std::string fsrc = GL_WRAP_STRINGFY
	//							(
	//								uniform sampler2D u_texture;
	//								uniform float     u_scale;

	//								varying vec2 v_texcoord;

	//								void main(void)
	//								{
	//									gl_FragColor = vec4(u_scale) * texture2D(u_texture, v_texcoord);
	//								}
	//							);

	//							p.set(vsrc, fsrc);
	//						}

	//						static void linear(program & p)
	//						{
	//							const std::string vsrc = GL_WRAP_STRINGFY
	//							(
	//								varying vec2 v_texcoord;

	//								void main(void)
	//								{
	//									v_texcoord  = gl_MultiTexCoord0.xy;
	//									gl_Position = gl_Vertex;
	//								}
	//							);

	//							const std::string fsrc = GL_WRAP_STRINGFY
	//							(
	//								uniform sampler2D u_texture;
	//								uniform float     u_bias;
	//								uniform float     u_scale;

	//								varying vec2 v_texcoord;

	//								void main(void)
	//								{
	//									gl_FragColor = vec4(u_bias) + vec4(u_scale) * texture2D(u_texture, v_texcoord);
	//								}
	//							);

	//							p.set(vsrc, fsrc);
	//						}

	//						static void map(program & p, const std::string & map_expression = std::string("{ result = value; }"))
	//						{
	//							const std::string vsrc = GL_WRAP_STRINGFY
	//							(
	//								varying vec2 v_texcoord;

	//								void main(void)
	//								{
	//									v_texcoord  = gl_MultiTexCoord0.xy;
	//									gl_Position = gl_Vertex;
	//								}
	//							);

	//							const std::string fsrc_0 = GL_WRAP_STRINGFY
	//							(
	//								uniform sampler2D u_texture;

	//								varying vec2 v_texcoord;

	//								void main(void)
	//								{
	//									vec4 value = texture2D(u_texture, v_texcoord);
	//									vec4 result;
	//							);

	//							const std::string fsrc_1 = GL_WRAP_STRINGFY
	//							(
	//									gl_FragColor = result;
	//								}
	//							);

	//							const std::string fsrc = fsrc_0 + map_expression + fsrc_1;

	//							p.set(vsrc, fsrc);
	//						}
	//				};
	//		};

	//		class misc
	//		{
	//			public :

	//				typedef misc this_type;

	//				static void empty(void)
	//				{
	//					;
	//				}
	//		};
	//};

}

#endif
